package com.wtao.testcamera.camera;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.view.Surface;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;


import com.wtao.testcamera.view.AutoFitTextureView;

import java.util.Arrays;

/**
 * 这个类通过Camera2Activity 验证发现是有问题的，预览几秒后就卡住了（卡住的原因是OnImageAvailableListener没有关闭ImageReader获取的图片）
 */
public class CameraV2Proxy {

    public static final String TAG = "CameraV2Proxy";

    private CameraDevice mCameraDevice;
    private String mCameraId = String.valueOf(CameraCharacteristics.LENS_FACING_BACK);
    private Size mPreviewSize;
    private HandlerThread mCameraThread;
    private Handler mCameraHandler;
    private Surface mSurface;
    private CaptureRequest.Builder mCaptureRequestBuilder;
    private CaptureRequest mCaptureRequest;
    private CameraCaptureSession mCameraCaptureSession;

    private ImageReader mImageReader;
    private ImageReader.OnImageAvailableListener mOnImageAvailableListener;

    private Context mContext;
    private AutoFitTextureView mTextureView;

    public CameraV2Proxy(Context context, AutoFitTextureView textureView) {
        mContext = context;
        mTextureView = textureView;
        startCameraThread();
    }

    public void startCameraThread() {
        mCameraThread = new HandlerThread("CameraThread");
        mCameraThread.start();
        mCameraHandler = new Handler(mCameraThread.getLooper());
    }

    @SuppressLint("MissingPermission")
    public boolean openCamera() {
        CameraManager cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        try {
            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
            Log.e(TAG, "openCamera: "+mCameraId+" PreviewWidth="+mPreviewSize.getWidth()+ " PreviewHeight="+mPreviewSize.getHeight());
            Log.e(TAG, "openCamera: mTextureView.getWidth="+mTextureView.getWidth()+" TextureView.getHeight="+ mTextureView.getHeight());
            int orientation = mContext.getResources().getConfiguration().orientation;
            if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            } else {
                mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
            }
            //旋转预览效果有90度偏差的问题
            configureTransform(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            cameraManager.openCamera(mCameraId, mStateCallback, mCameraHandler);
        } catch (Exception e) {
            Log.e(TAG, "openCamera: "+e.getMessage());
            e.printStackTrace();
            return false;
        }
        return true;
    }
    //旋转屏幕画面
    private void configureTransform(int viewWidth, int viewHeight) {
        Activity activity = (Activity) mContext;
        if (null == mTextureView || mPreviewSize == null || null == activity) {
            return;
        }
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        Matrix matrix = new Matrix();
        RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
        RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
        float centerX = viewRect.centerX();
        float centerY = viewRect.centerY();
        if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
            bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
            matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
            float scale = Math.max(
                    (float) viewHeight / mPreviewSize.getHeight(),
                    (float) viewWidth / mPreviewSize.getWidth());
            matrix.postScale(scale, scale, centerX, centerY);
            matrix.postRotate(90 * (rotation - 2), centerX, centerY);
        } else if (Surface.ROTATION_180 == rotation) {
            matrix.postRotate(180, centerX, centerY);
        }
        mTextureView.setTransform(matrix);
    }
    public CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            Log.e(TAG, "StateCallback onOpened: " );
            mCameraDevice = camera;
            startPreview();

            //openCamera()里有调用configureTransform，但是真正起效果的好像还是这里
            if (null != mTextureView) {
                configureTransform(mTextureView.getWidth(), mTextureView.getHeight());
            }
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            Log.e(TAG, "StateCallback onDisconnected: " );
            camera.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            Log.e(TAG, "StateCallback onError: " +error);
            camera.close();
            mCameraDevice = null;
        }
    };


    public void setPreview(Surface surface, ImageReader.OnImageAvailableListener listener) {
        mSurface = surface;
        mOnImageAvailableListener = listener;
    }

    public void startPreview() {
        try {
            mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

            mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, 2);

            mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mCameraHandler);
            mCaptureRequestBuilder.addTarget(mImageReader.getSurface());
            mCaptureRequestBuilder.addTarget(mSurface);
            mCameraDevice.createCaptureSession(Arrays.asList(mSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession session) {
                    try {

                        mCaptureRequest = mCaptureRequestBuilder.build();
                        mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                        mCameraCaptureSession = session;
                        mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession session) {

                }
            }, mCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    public void setPreviewSize(int width, int height) {
        mPreviewSize = new Size(width, height);

        Log.e(TAG, "setPreviewSize==>"+mTextureView.getWidth()+" "+mTextureView.getHeight());
    }

    public Handler getCameraHandler() {
        return mCameraHandler;
    }

    public CameraDevice getCameraDevice() {
        return mCameraDevice;
    }

    public int getOrientation() {
        try {
            CameraManager cameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
            CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(mCameraId);
            return characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        return -1;
    }

    public void releaseCamera() {
        closePreviewSession();

        if (null != mCameraDevice) {
            mCameraDevice.close();
            mCameraDevice = null;
        }
        mCameraThread.quitSafely();
    }

    private void closePreviewSession() {
        if (mCameraCaptureSession != null && mCameraDevice != null) {
            try {
                mCameraCaptureSession.stopRepeating();
                mCameraCaptureSession.abortCaptures();
                mCameraCaptureSession.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

